home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / tde210.zip / ED.C < prev    next >
C/C++ Source or Header  |  1992-11-13  |  64KB  |  2,059 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY rewritten.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * an array of pointers to functions.  We know what function to call as soon
  32.  * as a key is pressed.  It is also makes it easier to implement a configuration
  33.  * utility and macros.
  34.  *
  35.  * I added a few functions that I use quite often and I deleted a few that I
  36.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.   Deleted
  37.  * are Goto Marker 0-9 (others?).
  38.  *
  39.  * ************ In TDE 1.3, I put Goto Marker 0-9 back in.  ***************
  40.  *
  41.  * I felt that the insert routine should be separated into two routines.  One
  42.  * for inserting the various combinations of newlines and one for inserting
  43.  * ASCII and extended ASCII characters.
  44.  *
  45.  * One of Doug's design considerations was keeping screen updates to a minimum.
  46.  * I have expanded upon that idea and added support for updating windows
  47.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  48.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  49.  * or modifying text in one window may affect text in other windows - GLOBAL
  50.  * update.  Sometimes, updates to the current window are handled in the task
  51.  * routines so updates to other windows are done NOT_LOCALly.
  52.  *
  53.  * Also note that using functions copy_line and un_copy_line to change a line
  54.  * automatically adjusts the g_status.end_mem pointer.  If a function bypasses
  55.  * those functions, adjusting the g_status.end_mem pointer must be done
  56.  * explicitly.
  57.  *
  58.  * New editor name:  TDE, the Thomson-Davis Editor.
  59.  * Author:           Frank Davis
  60.  * Date:             June 5, 1991, version 1.0
  61.  * Date:             July 29, 1991, version 1.1
  62.  * Date:             October 5, 1991, version 1.2
  63.  * Date:             January 20, 1992, version 1.3
  64.  * Date:             February 17, 1992, version 1.4
  65.  * Date:             April 1, 1992, version 1.5
  66.  * Date:             June 5, 1992, version 2.0
  67.  * Date:             October 31, 1992, version 2.1
  68.  *
  69.  * This modification of Douglas Thomson's code is released into the
  70.  * public domain, Frank Davis.   You may distribute it freely.
  71.  */
  72.  
  73. #include "tdestr.h"     /* typedefs for global variables */
  74. #include "define.h"     /* editor function defs */
  75. #include "tdefunc.h"    /* prototypes for all functions in tde */
  76. #include "global.h"     /* global variables */
  77. #include "prompts.h"    /* prompt assignments */
  78. #include "default.h"    /* default function key assignments */
  79.  
  80.  
  81. /*
  82.  * Name:    insert_newline
  83.  * Purpose: insert a newline
  84.  * Date:    June 5, 1991
  85.  * Passed:  window:  pointer to current window
  86.  * Notes:   There a several ways to insert a line into a file:  1) pressing
  87.  *          a key, 2) word wrap, 3) any others?
  88.  *          When doing word wrap or format paragraph, don't show any changes.
  89.  *            Wait until the function finishes then show all changes at once.
  90.  */
  91. int  insert_newline( WINDOW *window )
  92. {
  93. char *source;       /* source for block move to make room for c */
  94. char *dest;         /* destination for block move */
  95. int len;            /* length of current line */
  96. int add;            /* characters to be added (usually 1 in insert mode) */
  97. int rcol;
  98. text_ptr prev;      /* previous lines scanned for autoindent */
  99. long length;
  100. int carriage_return;
  101. int split_line;
  102. int wordwrap;
  103. int dirty;
  104. int old_bcol;
  105. register WINDOW *win;  /* put window pointer in a register */
  106.  
  107.    win = window;
  108.    length = win->file_info->length;
  109.    if (win->rline > length && *win->cursor != CONTROL_Z)
  110.       return( OK );
  111.    wordwrap = mode.word_wrap;
  112.    switch (g_status.command) {
  113.       case WordWrap:
  114.          carriage_return = TRUE;
  115.          split_line = FALSE;
  116.          break;
  117.       case Rturn :
  118.          show_ruler_char( win );
  119.          carriage_return = TRUE;
  120.          split_line = FALSE;
  121.          break;
  122.       case AddLine :
  123.          split_line = carriage_return = FALSE;
  124.          break;
  125.       case SplitLine :
  126.          split_line = carriage_return = TRUE;
  127.          break;
  128.    }
  129.  
  130.    /*
  131.     * make window temporarily invisible to the un_copy_line function
  132.     */
  133.    win->visible = FALSE;
  134.    win->cursor = cpf( win->cursor );
  135.    copy_line( win->cursor, win->bottom_line );
  136.    detab_linebuff( );
  137.    len = linelen( g_status.line_buff );
  138.  
  139.    source = g_status.line_buff + len;
  140.    if (carriage_return || split_line) {
  141.       if (win->rcol < len)
  142.          source = g_status.line_buff + win->rcol;
  143.    }
  144.    /*
  145.     *  make room for '\n' just after source (source+1)
  146.     */
  147.    memmove( source+1, source, linelen( source )+2 );
  148.  
  149.    *source = '\n';
  150.    entab_linebuff( );
  151.    un_copy_line( win->cursor, win, TRUE );
  152.    adjust_windows_cursor( win, 1 );
  153.  
  154.    ++win->file_info->length;
  155.    win->file_info->dirty = NOT_LOCAL;
  156.    if (length == 0l || wordwrap || win->cline == win->bottom_line)
  157.       win->file_info->dirty = GLOBAL;
  158.    else if (!split_line)
  159.       update_line( win );
  160.  
  161.    /*
  162.     * If the cursor is to move down to the next line, then update
  163.     *  the line and column appropriately.
  164.     */
  165.    if (carriage_return || split_line) {
  166.       dirty = win->file_info->dirty;
  167.       prev = win->cursor;
  168.       win->cursor = find_next( win->cursor );
  169.       if (win->cline < win->bottom_line)
  170.          win->cline++;
  171.       win->rline++;
  172.       rcol = win->rcol;
  173.       old_bcol = win->bcol;
  174.  
  175.       if (mode.indent || wordwrap) {
  176.          /*
  177.           * autoindentation is required. Match the indentation of
  178.           *  the first line above that is not blank.
  179.           */
  180.          add = find_left_margin( wordwrap == FIXED_WRAP ? window->cursor : prev,
  181.                                  wordwrap );
  182.          copy_line( win->cursor, win->bottom_line );
  183.          detab_linebuff( );
  184.          len = linelen( g_status.line_buff );
  185.          source = g_status.line_buff;
  186.          if (len + add > MAX_LINE_LENGTH)
  187.             add = MAX_LINE_LENGTH - len;
  188.          dest = source + add;
  189.          memmove( dest, source, len+2 );
  190.  
  191.          /*
  192.           * now put in the autoindent characters
  193.           */
  194.          memset( source, ' ', add );
  195.          win->rcol = add;
  196.          entab_linebuff( );
  197.          un_copy_line( win->cursor, win, TRUE );
  198.       } else
  199.          win->rcol = 0;
  200.       if (split_line) {
  201.          win->cursor = cpb( win->cursor );
  202.          win->cursor = find_prev( win->cursor );
  203.          if (win->cline > win->top_line + window->ruler)
  204.             win->cline--;
  205.          win->rline--;
  206.          win->rcol = rcol;
  207.       }
  208.       check_virtual_col( win, win->rcol, win->ccol );
  209.       if (dirty == GLOBAL || win->file_info->dirty == LOCAL || wordwrap)
  210.          win->file_info->dirty = GLOBAL;
  211.       else
  212.          win->file_info->dirty = dirty;
  213.    }
  214.  
  215.    /*
  216.     * record that file has been modified
  217.     */
  218.    if (win->file_info->dirty != GLOBAL)
  219.       my_scroll_down( win );
  220.    restore_marked_block( win, 1 );
  221.    show_size( win );
  222.    win->visible = TRUE;
  223.    if (old_bcol != win->bcol) {
  224.       make_ruler( win );
  225.       show_ruler( win );
  226.    }
  227.    return( OK );
  228. }
  229.  
  230.  
  231. /*
  232.  * Name:    insert_overwrite
  233.  * Purpose: To make the necessary changes after the user has typed a normal
  234.  *           printable character
  235.  * Date:    June 5, 1991
  236.  * Passed:  window:  pointer to current window
  237.  */
  238. int  insert_overwrite( WINDOW *window )
  239. {
  240. char *source;       /* source for block move to make room for c */
  241. char *dest;         /* destination for block move */
  242. int len;            /* length of current line */
  243. int pad;            /* padding to add if cursor beyond end of line */
  244. int add;            /* characters to be added (usually 1 in insert mode) */
  245. register int rcol;
  246. register WINDOW *win;  /* put window pointer in a register */
  247. int rc;
  248.  
  249.    win = window;
  250.    if (*win->cursor == CONTROL_Z || g_status.key_pressed >= 256)
  251.       rc = OK;
  252.    else {
  253.       rcol = win->rcol;
  254.       /*
  255.        * first check we have room - the editor can not
  256.        *  cope with lines wider than g_display.line_length
  257.        */
  258.       if (rcol >= g_display.line_length) {
  259.          /*
  260.           * cannot insert more characters
  261.           */
  262.          error( WARNING, win->bottom_line, ed2 );
  263.          rc = ERROR;
  264.       } else {
  265.          copy_line( win->cursor, win->bottom_line );
  266.          detab_linebuff( );
  267.  
  268.          /*
  269.           * work out how many characters need to be inserted
  270.           */
  271.          len = linelen( g_status.line_buff );
  272.          pad = rcol > len ? rcol - len : 0;
  273.  
  274.          /*
  275.           * if this is the last line in a file, the last character in the
  276.           * line buffer will be CONTROL_Z.  increment pad and insert a \n.
  277.           */
  278.          if (g_status.line_buff[len] == CONTROL_Z)
  279.             ++pad;
  280.  
  281.          if (mode.insert || rcol >= len)
  282.             /*
  283.              * inserted characters, or overwritten characters at the end of
  284.              *  the line, are inserted.
  285.              */
  286.             add = 1;
  287.          else
  288.             /*
  289.              *  and no extra space is required to overwrite existing characters
  290.              */
  291.             add = 0;
  292.  
  293.          /*
  294.           * check that current line would not get too long.
  295.           */
  296.          if (len + pad + add >= g_display.line_length) {
  297.             /*
  298.              * no more room to add
  299.              */
  300.             error( WARNING, win->bottom_line, ed3 );
  301.             rc = ERROR;
  302.          } else {
  303.  
  304.             /*
  305.              * make room for whatever needs to be inserted
  306.              */
  307.             if (pad > 0  || add > 0) {
  308.                source = g_status.line_buff + len;
  309.                if (*source == CONTROL_Z) {
  310.                   if (rcol > len)
  311.                      source = g_status.line_buff + rcol + 1;
  312.                   *source++ = '\n';
  313.                   *source   = CONTROL_Z;
  314.                   ++win->file_info->length;
  315.                   show_size( win );
  316.                   --pad;
  317.                   ++len;
  318.                }
  319.                source = g_status.line_buff + rcol - pad;
  320.                dest = source + pad + add;
  321.                memmove( dest, source, len + pad - rcol + 2 );
  322.                /*
  323.                 * put in the required padding
  324.                 */
  325.                memset( source, ' ', pad );
  326.             }
  327.             g_status.line_buff[rcol] = (char)g_status.key_pressed;
  328.             entab_linebuff( );
  329.  
  330.             /*
  331.              * always increment the real column (rcol) then adjust the
  332.              * logical and base column as needed.   show the changed line
  333.              * in all but the LOCAL window.  In the LOCAL window, there are
  334.              * two cases:  1) update the line, or 2) redraw the window if
  335.              * cursor goes too far right.
  336.              */
  337.             win->file_info->dirty = NOT_LOCAL;
  338.             show_changed_line( win );
  339.             if (win->ccol < win->end_col) {
  340.                show_curl_line( win );
  341.                show_ruler_char( win );
  342.                win->ccol++;
  343.             } else {
  344.                win->bcol++;
  345.                win->file_info->dirty = LOCAL;
  346.                make_ruler( win );
  347.                show_ruler( win );
  348.             }
  349.             rcol++;
  350.          }
  351.  
  352.          /*
  353.           * record that file has been modified and adjust cursors,
  354.           * file start and end pointers as needed.
  355.           */
  356.          check_virtual_col( win, rcol, win->ccol );
  357.          win->file_info->modified = TRUE;
  358.          if (mode.word_wrap) {
  359.             g_status.command = WordWrap;
  360.             word_wrap( win );
  361.          }
  362.          rc = OK;
  363.       }
  364.    }
  365.    return( rc );
  366. }
  367.  
  368.  
  369. /*
  370.  * Name:    join_line
  371.  * Purpose: To join current line and line below at cursor
  372.  * Date:    June 5, 1991
  373.  * Passed:  window:  pointer to current window
  374.  * Notes:   trunc the line then join with line below if it exists
  375.  */
  376. int  join_line( WINDOW *window )
  377. {
  378. register int len;   /* length of current line */
  379. text_ptr dest;         /* destination for block move */
  380. text_ptr p;         /* next line in file */
  381. text_ptr tab_free;  /* next line in file -- with the tabs removed */
  382. int pad;            /* padding spaces required */
  383. int cr;             /* position of hard return in main text buffer */
  384. register WINDOW *win;  /* put window pointer in a register */
  385. int rc;
  386.  
  387.    win = window;
  388.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  389.       return( OK );
  390.  
  391.    rc = OK;
  392.    win->cursor = cpf( win->cursor );
  393.    if ((p = find_next( win->cursor )) != NULL && *p != CONTROL_Z) {
  394.       cr = linelen( win->cursor );
  395.       load_undo_buffer( win->cursor );
  396.       copy_line( win->cursor, win->bottom_line );
  397.       detab_linebuff( );
  398.  
  399.       /*
  400.        * if cursor is in line before line feed, reset len to rcol
  401.        */
  402.       if (win->rcol < (len = linelen( g_status.line_buff )))
  403.          len = win->rcol;
  404.  
  405.       /*
  406.        * if there any tabs in the next line, expand them because we
  407.        *   probably have to redo them anyway.
  408.        */
  409.       tab_free = detab_a_line( p );
  410.  
  411.       /*
  412.        * add padding if required
  413.        */
  414.       pad = win->rcol > len ? win->rcol - len : 0;
  415.  
  416.  
  417.       /*
  418.        * check room to combine lines
  419.        */
  420.       if (len + pad + 1 + linelen( tab_free ) >= g_display.line_length) {
  421.          /*
  422.           * cannot combine lines.
  423.           */
  424.          error( WARNING, win->bottom_line, ed4 );
  425.          rc = ERROR;
  426.       } else {
  427.  
  428.          /*
  429.           * remove the '\n' separating the lines in the main text buffer
  430.           */
  431.          *(win->cursor + cr) = '\0';
  432.  
  433.  
  434.          dest = g_status.line_buff + len;
  435.          /*
  436.           * insert padding
  437.           */
  438.          if (pad > 0) {
  439.             while (pad--)
  440.                *dest++ = ' ';
  441.          }
  442.          len = linelen( tab_free );
  443.          _fmemcpy( dest, tab_free, len + 2 );
  444.          if (*(dest+len) == '\n')
  445.             *(dest+len+1) = CONTROL_Z;
  446.          entab_linebuff( );
  447.  
  448.          g_status.copied = TRUE;
  449.          un_copy_line( win->cursor, win, FALSE );
  450.          adjust_windows_cursor( win, -1 );
  451.          --win->file_info->length;
  452.          restore_marked_block( win, -1 );
  453.          show_size( win );
  454.  
  455.          win->file_info->dirty = GLOBAL;
  456.       }
  457.    }
  458.    return( rc );
  459. }
  460.  
  461.  
  462. /*
  463.  * Name:    word_delete
  464.  * Purpose: To delete from the cursor to the start of the next word.
  465.  * Date:    September 1, 1991
  466.  * Passed:  window:  pointer to current window
  467.  * Notes:   If the cursor is at the right of the line, then combine the
  468.  *           current line with the next one, leaving the cursor where it
  469.  *           is.
  470.  *          If the cursor is on an alphanumeric character, then all
  471.  *           subsequent alphanumeric characters are deleted.
  472.  *          If the cursor is on a space, then all subsequent spaces
  473.  *           are deleted.
  474.  *          If the cursor is on a punctuation character, then all
  475.  *           subsequent punctuation characters are deleted.
  476.  */
  477. int  word_delete( WINDOW *window )
  478. {
  479. int len;            /* length of current line */
  480. register int start; /* column that next word starts in */
  481. char *source;       /* source for block move to delete word */
  482. char *dest;         /* destination for block move */
  483. text_ptr p;
  484. register WINDOW *win;  /* put window pointer in a register */
  485.  
  486.    win = window;
  487.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  488.       return( OK );
  489.    win->cursor = cpf( win->cursor );
  490.    copy_line( win->cursor, win->bottom_line );
  491.    detab_linebuff( );
  492.    if (win->rcol >= (len = linelen( g_status.line_buff ))) {
  493.       join_line( win );
  494.       p = win->cursor + win->rcol;
  495.       if (*p != CONTROL_Z)
  496.          load_undo_buffer( p );
  497.    } else {
  498.       /*
  499.        * normal word delete
  500.        *
  501.        * find the start of the next word
  502.        */
  503.       start = win->rcol;
  504.       if (isspace( g_status.line_buff[start] )) {
  505.          /*
  506.           * the cursor was on a space, so eat all consecutive spaces
  507.           *  from the cursor onwards.
  508.           */
  509.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  510.             ++start;
  511.       } else {
  512.          /*
  513.           * eat all consecutive characters in the same class (spaces
  514.           *  are considered to be in the same class as the cursor
  515.           *  character)
  516.           */
  517.          while (start < len  &&  !isspace( g_status.line_buff[start] ))
  518.             ++start;
  519.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  520.             ++start;
  521.       }
  522.  
  523.       /*
  524.        * move text to delete word
  525.        */
  526.       source = g_status.line_buff + start;
  527.       dest = g_status.line_buff + win->rcol;
  528.       memmove( dest, source, len-start+2 );
  529.       entab_linebuff( );
  530.       win->file_info->modified = TRUE;
  531.       win->file_info->dirty = GLOBAL;
  532.       if (g_status.command == WordDelete)
  533.          show_changed_line( win );
  534.    }
  535.    return( OK );
  536. }
  537.  
  538.  
  539. /*
  540.  * Name:    dup_line
  541.  * Purpose: Duplicate current line
  542.  * Date:    June 5, 1991
  543.  * Passed:  window:  pointer to current window
  544.  * Notes:   cursor stays on current line
  545.  */
  546. int  dup_line( WINDOW *window )
  547. {
  548. register int len;       /* length of current line */
  549. long number;
  550. text_ptr d, s;
  551. register WINDOW *win;  /* put window pointer in a register */
  552. int  rc;
  553.  
  554.    win = window;
  555.    if (win->rline > win->file_info->length)
  556.       return( ERROR );
  557.    win->cursor = cpf( win->cursor );
  558.  
  559.    entab_linebuff( );
  560.    un_copy_line( win->cursor, win, TRUE );
  561.    /*
  562.     * don't dup the ^Z or a NULL line
  563.     */
  564.    if (*win->cursor != CONTROL_Z && (d=find_next( win->cursor )) != NULL) {
  565.  
  566.       /*
  567.        * don't use buffers to dup the line.  use hw_move to make space and
  568.        * copy current line at same time.  d is set to beginning of next line.
  569.        */
  570.       s = win->cursor;
  571.       len = linelen( s );
  572.       if (s[len] == '\n')
  573.          ++len;
  574.       number = ptoul( g_status.end_mem ) - ptoul( s );
  575.       hw_move( d, s, number );
  576.       ++win->file_info->length;
  577.       g_status.end_mem = addltop( len, g_status.end_mem );
  578.       adjust_start_end( win->file_info, len );
  579.       addorsub_all_cursors( win, len );
  580.       adjust_windows_cursor( win, 1 );
  581.  
  582.       /*
  583.        * if current line is the bottom line, we can't see the dup line because
  584.        * cursor doesn't move and dup line is added after current line.
  585.        */
  586.       if  (win->cline != win->bottom_line)
  587.          my_scroll_down( win );
  588.       win->file_info->dirty = NOT_LOCAL;
  589.  
  590.       /*
  591.        * record that file has been modified
  592.        */
  593.       win->file_info->modified = TRUE;
  594.       restore_marked_block( win, 1 );
  595.       show_size( win );
  596.       show_avail_mem( );
  597.       rc = OK;
  598.    } else {
  599.       /*
  600.        * cannot duplicate line
  601.        */
  602.       error( WARNING, win->bottom_line, ed5 );
  603.       rc = ERROR;
  604.    }
  605.    return( rc );
  606. }
  607.  
  608.  
  609. /*
  610.  * Name:    back_space
  611.  * Purpose: To delete the character to the left of the cursor.
  612.  * Date:    June 5, 1991
  613.  * Passed:  window:  pointer to current window
  614.  * Notes:   If the cursor is at the left of the line, then combine the
  615.  *           current line with the previous one.
  616.  *          If in indent mode, and the cursor is on the first non-blank
  617.  *           character of the line, then match the indentation of an
  618.  *           earlier line.
  619.  */
  620. int  back_space( WINDOW *window )
  621. {
  622. int len;            /* length of the current line */
  623. char *source;       /* source of block move to delete character */
  624. char *dest;         /* destination of block move */
  625. text_ptr p;         /* previous line in file */
  626. int plen;           /* length of previous line */
  627. int del_count;      /* number of characters to delete */
  628. int pos;            /* the position of the first non-blank char */
  629. register int rcol;
  630. int ccol;
  631. int old_bcol;
  632. register WINDOW *win;  /* put window pointer in a register */
  633.  
  634.    win = window;
  635.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  636.       return( ERROR );
  637.    win->cursor = cpf( win->cursor );
  638.    copy_line( win->cursor, win->bottom_line );
  639.    detab_linebuff( );
  640.    len = linelen( g_status.line_buff );
  641.    rcol = win->rcol;
  642.    ccol = win->ccol;
  643.    old_bcol = win->bcol;
  644.    if (rcol == 0) {
  645.       /*
  646.        * combine this line with the previous, if any
  647.        */
  648.       win->cursor = cpb( win->cursor );
  649.       if ((p = find_prev( win->cursor )) != NULL) {
  650.          if (len + 2 + (plen = linelen( p )) >= g_display.line_length) {
  651.             /*
  652.              * cannot combine lines
  653.              */
  654.             error( WARNING, win->bottom_line, ed4 );
  655.             return( ERROR );
  656.          }
  657.  
  658.          /*
  659.           * current line has already been copied into line_buff.  let's
  660.           *  make sure that line_buff get copied into tab_buff,
  661.           *  regardless of mode.inflate_tabs.
  662.           */
  663.          memmove( g_status.tabout_buff, g_status.line_buff, len+2 );
  664.  
  665.          /*
  666.           * now copy the previous line into line_buff.  rcol should be
  667.           *   set the end of the previous line.
  668.           */
  669.          p = cpf( p );
  670.          g_status.copied = FALSE;
  671.          copy_line( p, win->bottom_line );
  672.          load_undo_buffer( p );
  673.          rcol = find_end( p );
  674.  
  675.          /*
  676.           * now, copy the line in tabout_buff to end of line_buff.  then
  677.           *   detab and retab the joined line.
  678.           */
  679.          memmove( g_status.line_buff+plen, g_status.tabout_buff, len+2 );
  680.          detab_linebuff( );
  681.          entab_linebuff( );
  682.  
  683.          /*
  684.           * now, replace the '\n' in the main text buffer with a null
  685.           *   character.
  686.           */
  687.          *(p+plen) = '\0';
  688.  
  689.          if (win->cline > win->top_line + win->ruler)
  690.             --win->cline;
  691.          --win->rline;
  692.          win->cursor = p;
  693.          un_copy_line( win->cursor, win, TRUE );
  694.  
  695.          /*
  696.           * make sure cursor stays on the screen, at the end of the
  697.           *  previous line
  698.           */
  699.          ccol = rcol - win->bcol;
  700.          --win->file_info->length;
  701.          restore_marked_block( win, -1 );
  702.          adjust_windows_cursor( win, -1 );
  703.          show_size( win );
  704.          check_virtual_col( win, rcol, ccol );
  705.          win->file_info->dirty = GLOBAL;
  706.          make_ruler( win );
  707.          show_ruler( win );
  708.       } else
  709.          return( ERROR );
  710.    } else {
  711.       /*
  712.        * normal delete
  713.        *
  714.        * find out how much to delete (depends on indent mode)
  715.        */
  716.       del_count = 1;   /* the default */
  717.       if (mode.indent) {
  718.          /*
  719.           * indent only happens if the cursor is on the first
  720.           *  non-blank character of the line
  721.           */
  722.          if ((pos = first_non_blank( g_status.line_buff )) == rcol
  723.                     || g_status.line_buff[pos] == '\n'
  724.                     || g_status.line_buff[pos] == CONTROL_Z) {
  725.             /*
  726.              * now work out how much to indent
  727.              */
  728.             p = cpb( win->cursor );
  729.             for (p=find_prev( p ); p != NULL; p=find_prev( p )) {
  730.                if ((plen=first_non_blank( p )) < rcol && *(p+plen)!='\n') {
  731.                   /*
  732.                    * found the line to match
  733.                    */
  734.                   del_count = rcol - plen;
  735.                   break;
  736.                }
  737.             }
  738.          }
  739.       }
  740.  
  741.       /*
  742.        * move text to delete char(s), unless no chars actually there
  743.        */
  744.       if (rcol - del_count < len) {
  745.          dest = g_status.line_buff + rcol - del_count;
  746.          if (rcol > len) {
  747.             source = g_status.line_buff + len;
  748.             len = 2;
  749.          } else {
  750.             source = g_status.line_buff + rcol;
  751.             len = len - rcol + 2;
  752.          }
  753.          memmove( dest, source, len );
  754.          entab_linebuff( );
  755.       }
  756.       rcol -= del_count;
  757.       ccol -= del_count;
  758.       win->file_info->dirty = NOT_LOCAL;
  759.       show_ruler_char( win );
  760.       show_changed_line( win );
  761.       check_virtual_col( win, rcol, ccol );
  762.       if (!win->file_info->dirty)
  763.          show_curl_line( win );
  764.       if (old_bcol != win->bcol) {
  765.          make_ruler( win );
  766.          show_ruler( win );
  767.       }
  768.    }
  769.    win->file_info->modified = TRUE;
  770.    return( OK );
  771. }
  772.  
  773.  
  774. /*
  775.  * Name:    line_kill
  776.  * Purpose: To delete the line the cursor is on.
  777.  * Date:    June 5, 1991
  778.  * Passed:  window:  pointer to current window
  779.  * Notes:   If *window->cursor is pointing to CONTROL_Z then do not do a
  780.  *          line kill (can't kill a NULL line).
  781.  */
  782. int  line_kill( WINDOW *window )
  783. {
  784. int i = 0;
  785. text_ptr s;         /* next line in file */
  786. register WINDOW *win;  /* put window pointer in a register */
  787.  
  788.    win = window;
  789.    if (win->file_info->length > 0  && *win->cursor != CONTROL_Z) {
  790.       s = win->cursor = cpf( win->cursor );
  791.       load_undo_buffer( g_status.copied ? g_status.line_buff : win->cursor );
  792.  
  793.       /*
  794.        * to delete a line, put a CONTROL_Z as the first character in the
  795.        *   line_buff -- no line feeds.
  796.        */
  797.       g_status.copied = TRUE;
  798.       g_status.line_buff[0] = CONTROL_Z;
  799.  
  800.       /*
  801.        * if line to delete has \n at end of line then decrement file length.
  802.        */
  803.       if (*(s + linelen( s )) == '\n') {
  804.          --win->file_info->length;
  805.          --i;
  806.       }
  807.       un_copy_line( s, win, FALSE );
  808.       win->file_info->dirty = NOT_LOCAL;
  809.  
  810.       /*
  811.        * move all cursors one according to i, restore begin and end block
  812.        */
  813.       adjust_windows_cursor( win, i );
  814.       restore_marked_block( win, i );
  815.  
  816.       /*
  817.        * we are not doing a GLOBAL update, so update current window here
  818.        */
  819.       if (win->file_info->dirty == NOT_LOCAL)
  820.          my_scroll_down( win );
  821.       show_size( win );
  822.       return( OK );
  823.    } else
  824.       return( ERROR );
  825. }
  826.  
  827.  
  828. /*
  829.  * Name:    char_del_under
  830.  * Purpose: To delete the character under the cursor.
  831.  * Date:    June 5, 1991
  832.  * Passed:  window:  pointer to current window
  833.  * Notes:   If the cursor is beyond the end of the line, then this
  834.  *           command is ignored.
  835.  *          DeleteChar and StreamDeleteChar use this function.
  836.  */
  837. int  char_del_under( WINDOW *window )
  838. {
  839. char *source;    /* source of block move to delete character */
  840. register int len;
  841. register WINDOW *win;  /* put window pointer in a register */
  842.  
  843.    win = window;
  844.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  845.       return( OK );
  846.    copy_line( win->cursor, win->bottom_line );
  847.    detab_linebuff( );
  848.    if (win->rcol < (len = linelen( g_status.line_buff ))) {
  849.       /*
  850.        * move text to delete using buffer
  851.        */
  852.       source = g_status.line_buff + win->rcol + 1;
  853.       memmove( source-1, source, len - win->rcol + 2 );
  854.       entab_linebuff( );
  855.       win->file_info->dirty    = GLOBAL;
  856.       win->file_info->modified = TRUE;
  857.       show_changed_line( win );
  858.    } else if (g_status.command == StreamDeleteChar)
  859.       join_line( win );
  860.    return( OK );
  861. }
  862.  
  863.  
  864. /*
  865.  * Name:    eol_kill
  866.  * Purpose: To delete everything from the cursor to the end of the line.
  867.  * Date:    June 5, 1991
  868.  * Passed:  window:  pointer to current window
  869.  * Notes:   If the cursor is beyond the end of the line, then this
  870.  *           command is ignored.
  871.  */
  872. int  eol_kill( WINDOW *window )
  873. {
  874. register char *dest;  /* the start of the delete area */
  875. register WINDOW *win;  /* put window pointer in a register */
  876.  
  877.    win = window;
  878.    if (win->rline > win->file_info->length || *win->cursor == CONTROL_Z)
  879.       return( OK );
  880.    copy_line( win->cursor, win->bottom_line );
  881.    detab_linebuff( );
  882.    load_undo_buffer( g_status.line_buff );
  883.    if ((unsigned)win->rcol < linelen( g_status.line_buff )) {
  884.       /*
  885.        * truncate to delete rest of line
  886.        */
  887.       dest = g_status.line_buff + win->rcol;
  888.       *dest++ = '\n';
  889.       *dest = CONTROL_Z;
  890.       entab_linebuff( );
  891.       win->file_info->dirty = GLOBAL;
  892.       show_changed_line( win );
  893.    }
  894.    return( OK );
  895. }
  896.  
  897.  
  898. /*
  899.  * Name:    undo_line
  900.  * Purpose: To retrieve unaltered line if possible.
  901.  * Date:    June 5, 1991
  902.  * Passed:  window:  pointer to current window
  903.  * Notes:   Changes are made to the line buffer so the underlying text has
  904.  *          not changed.  Put the unchanged line from the file into the
  905.  *          line buffer and display it.
  906.  */
  907. int  undo_line( WINDOW *window )
  908. {
  909. register WINDOW *win;  /* put window pointer in a register */
  910.  
  911.    win = window;
  912.    if (win->rline <= win->file_info->length  &&  g_status.copied) {
  913.       g_status.copied = FALSE;
  914.       copy_line( win->cursor, win->bottom_line );
  915.       detab_linebuff( );
  916.       win->file_info->dirty = GLOBAL;
  917.       show_changed_line( win );
  918.    }
  919.    return( OK );
  920. }
  921.  
  922.  
  923. /*
  924.  * Name:    undo
  925.  * Purpose: To retrieve (pop) a line from the undo stack
  926.  * Date:    September 26, 1991
  927.  * Passed:  window:  pointer to current window
  928.  * Notes:   Insert an empty line into the file then pop the line in the undo
  929.  *          stack.  When we pop line 0, there are no more lines on the stack.
  930.  *          Set the stack pointer to -1 to indicate an empty stack.
  931.  */
  932. int  undo( WINDOW *window )
  933. {
  934. char *source;    /* pointer to line_buff */
  935. unsigned int len;
  936. unsigned int number;
  937. register WINDOW *win;  /* put window pointer in a register */
  938.  
  939.    if (g_status.bot_stack != NULL) {
  940.       win = window;
  941.       win->cursor = cpf( win->cursor );
  942.       copy_line( win->cursor, win->bottom_line );
  943.       detab_linebuff( );
  944.       entab_linebuff( );
  945.  
  946.       /*
  947.        *  make room for '\n'.  then, after we un_copy the g_status.line_buff, we
  948.        *  have added a line to the file.
  949.        */
  950.       source = g_status.line_buff;
  951.       number = linelen( source ) + 2;
  952.       len = linelen( g_status.top_stack ) + 1;
  953.       memmove( source+len, source, number );
  954.       _fmemcpy( source, g_status.top_stack, len );
  955.       un_copy_line( win->cursor, win, TRUE );
  956.  
  957.       /*
  958.        *  ajust cursors in other windows opened to the same file.
  959.        */
  960.       adjust_windows_cursor( win, 1 );
  961.  
  962.  
  963.       /*
  964.        * we have now undeleted a line.  increment the file length and display
  965.        * it.
  966.        */
  967.       win->file_info->length++;
  968.       win->file_info->dirty = GLOBAL;
  969.       show_size( win );
  970.  
  971.       /*
  972.        * now "pop" the line off the stack.
  973.        */
  974.       number = (unsigned)(ptoul( g_status.bot_stack ) -
  975.                           ptoul( g_status.top_stack )) - len;
  976.       hw_move( g_status.top_stack, g_status.top_stack+len, number );
  977.  
  978.       /*
  979.        * bottom of stack now moves up.  it the bottom of the stack
  980.        *   is equal to top of stack, then the stack is empty.
  981.        */
  982.       g_status.bot_stack = cpb( g_status.bot_stack ) - len;
  983.       if (ptoul( g_status.bot_stack ) == ptoul( g_status.top_stack ))
  984.          g_status.bot_stack = NULL;
  985.    }
  986.    return( OK );
  987. }
  988.  
  989.  
  990. /*
  991.  * Name:    beg_next_line
  992.  * Purpose: To move the cursor to the beginning of the next line.
  993.  * Date:    October 4, 1991
  994.  * Passed:  window:  pointer to current window
  995.  */
  996. int  beg_next_line( WINDOW *window )
  997. {
  998. int rc;
  999.  
  1000.    window->rcol = 0;
  1001.    rc = prepare_move_down( window );
  1002.    check_virtual_col( window, window->rcol, window->ccol );
  1003.    sync( window );
  1004.    make_ruler( window );
  1005.    show_ruler( window );
  1006.    return( rc );
  1007. }
  1008.  
  1009.  
  1010. /*
  1011.  * Name:    next_line
  1012.  * Purpose: To move the cursor to the first character of the next line.
  1013.  * Date:    October 4, 1991
  1014.  * Passed:  window:  pointer to current window
  1015.  */
  1016. int  next_line( WINDOW *window )
  1017. {
  1018. register int rcol;
  1019. register WINDOW *win;  /* put window pointer in a register */
  1020. int rc;
  1021.  
  1022.    win = window;
  1023.    rc = prepare_move_down( win );
  1024.    rcol = first_non_blank( win->cursor );
  1025.    if (win->cursor[rcol] == '\n')
  1026.       rcol = 0;
  1027.    check_virtual_col( win, rcol, win->ccol );
  1028.    sync( win );
  1029.    make_ruler( win );
  1030.    show_ruler( win );
  1031.    return( rc );
  1032. }
  1033.  
  1034.  
  1035. /*
  1036.  * Name:    home
  1037.  * Purpose: To move the cursor to the left of the current line.
  1038.  * Date:    June 5, 1991
  1039.  * Passed:  window:  pointer to current window
  1040.  * Notes:   this routine is made a little more complicated with cursor sync.
  1041.  *            if the g_status.copied flag is set we need to see from what file
  1042.  *            the line_buff was copied.
  1043.  */
  1044. int  home( WINDOW *window )
  1045. {
  1046. register int rcol;
  1047. register WINDOW *win;  /* put window pointer in a register */
  1048.  
  1049.    win = window;
  1050.    if (g_status.copied && win->file_info == g_status.current_window->file_info){
  1051.       rcol = first_non_blank( g_status.line_buff );
  1052.       if (is_line_blank( g_status.line_buff ))
  1053.          rcol = 0;
  1054.    } else {
  1055.       win->cursor = cpf( win->cursor );
  1056.       rcol = first_non_blank( win->cursor );
  1057.       if (is_line_blank( win->cursor ))
  1058.          rcol = 0;
  1059.    }
  1060.    if (win->rcol == rcol)
  1061.       rcol = 0;
  1062.    check_virtual_col( win, rcol, win->ccol );
  1063.    sync( win );
  1064.    make_ruler( win );
  1065.    show_ruler( win );
  1066.    return( OK );
  1067. }
  1068.  
  1069.  
  1070. /*
  1071.  * Name:    goto_eol
  1072.  * Purpose: To move the cursor to the eol character of the current line.
  1073.  * Date:    June 5, 1991
  1074.  * Passed:  window:  pointer to current window
  1075.  * Notes:   this routine is made a little more complicated with cursor sync.
  1076.  *            if the g_status.copied flag is set we need to see from what file
  1077.  *            the line_buff was copied.
  1078.  */
  1079. int  goto_eol( WINDOW *window )
  1080. {
  1081. register int rcol;
  1082. register WINDOW *win;  /* put window pointer in a register */
  1083.  
  1084.    win = window;
  1085.    if (g_status.copied) {
  1086.       if (win->file_info == g_status.current_window->file_info)
  1087.          rcol = find_end( g_status.line_buff );
  1088.       else
  1089.          rcol = find_end( win->cursor );
  1090.    } else
  1091.       rcol = find_end( win->cursor );
  1092.    win->ccol = win->start_col + rcol - win->bcol;
  1093.    check_virtual_col( win, rcol, win->ccol );
  1094.    sync( win );
  1095.    make_ruler( win );
  1096.    show_ruler( win );
  1097.    return( OK );
  1098. }
  1099.  
  1100.  
  1101. /*
  1102.  * Name:    goto_top
  1103.  * Purpose: To move the cursor to the top of the current window.
  1104.  * Date:    June 5, 1991
  1105.  * Passed:  window:  pointer to current window
  1106.  * Notes:   If the start of the file occurs before the top of the window,
  1107.  *           then the start of the file is moved to the top of the window.
  1108.  */
  1109. int  goto_top( WINDOW *window )
  1110. {
  1111. text_ptr cursor;  /* anticipated cursor line */
  1112. register WINDOW *win;  /* put window pointer in a register */
  1113.  
  1114.    win = window;
  1115.    un_copy_line( win->cursor, win, TRUE );
  1116.    update_line( win );
  1117.    win->cursor = cpb( win->cursor );
  1118.    for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
  1119.       if ((cursor = find_prev( win->cursor )) == NULL)
  1120.          break;
  1121.       win->cursor = cursor;
  1122.    }
  1123.    show_curl_line( win );
  1124.    sync( win );
  1125.    return( OK );
  1126. }
  1127.  
  1128.  
  1129. /*
  1130.  * Name:    goto_bottom
  1131.  * Purpose: To move the cursor to the bottom of the current window.
  1132.  * Date:    June 5, 1991
  1133.  * Passed:  window:  pointer to current window
  1134.  */
  1135. int  goto_bottom( WINDOW *window )
  1136. {
  1137. text_ptr cursor;
  1138. register WINDOW *win;  /* put window pointer in a register */
  1139.  
  1140.    win = window;
  1141.    un_copy_line( win->cursor, win, TRUE );
  1142.    update_line( win );
  1143.    win->cursor = cpf( win->cursor );
  1144.    for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
  1145.       if ((cursor = find_next( win->cursor )) == NULL ||
  1146.                     find_next( cursor ) == NULL)
  1147.          break;
  1148.       win->cursor = cursor;
  1149.    }
  1150.    show_curl_line( win );
  1151.    sync( win );
  1152.    return( OK );
  1153. }
  1154.  
  1155.  
  1156. /*
  1157.  * Name:    set_tabstop
  1158.  * Purpose: To set the current interval between tab stops
  1159.  * Date:    October 1, 1989
  1160.  * Notes:   Tab interval must be reasonable, and this function will
  1161.  *           not allow tabs more than MAX_COLS / 2.
  1162.  */
  1163. int  set_tabstop( WINDOW *window )
  1164. {
  1165. char num_str[MAX_COLS];  /* tab interval as a character string */
  1166. int tab;                 /* new tab interval */
  1167. register int rc;
  1168. register file_infos *file;
  1169.  
  1170.    itoa( mode.ltab_size, num_str, 10 );
  1171.    /*
  1172.     * tab interval:
  1173.     */
  1174.    rc = get_name( ed7a, window->bottom_line, num_str, g_display.message_color );
  1175.    if (rc == OK) {
  1176.       tab = atoi( num_str );
  1177.       if (tab < MAX_COLS/2) {
  1178.          mode.ltab_size = tab;
  1179.          if (mode.inflate_tabs) {
  1180.             for (file=g_status.file_list; file != NULL; file=file->next)
  1181.                file->dirty = GLOBAL;
  1182.          }
  1183.       } else {
  1184.          /*
  1185.           * tab size too long
  1186.           */
  1187.          error( WARNING, window->bottom_line, ed8 );
  1188.          rc = ERROR;
  1189.       }
  1190.    }
  1191.  
  1192.    itoa( mode.ptab_size, num_str, 10 );
  1193.    /*
  1194.     * tab interval:
  1195.     */
  1196.    rc = get_name( ed7b, window->bottom_line, num_str, g_display.message_color );
  1197.    if (rc == OK) {
  1198.       tab = atoi( num_str );
  1199.       if (tab < MAX_COLS/2) {
  1200.          mode.ptab_size = tab;
  1201.          show_tab_modes( );
  1202.          if (mode.inflate_tabs) {
  1203.             for (file=g_status.file_list; file != NULL; file=file->next)
  1204.                file->dirty = GLOBAL;
  1205.          }
  1206.       } else {
  1207.          /*
  1208.           * tab size too long
  1209.           */
  1210.          error( WARNING, window->bottom_line, ed8 );
  1211.          rc = ERROR;
  1212.       }
  1213.    }
  1214.    return( rc );
  1215. }
  1216.  
  1217.  
  1218. /*
  1219.  * Name:    show_line_col
  1220.  * Purpose: show current real line and column of current cursor position
  1221.  * Date:    June 5, 1991
  1222.  * Passed:  window:  pointer to current window
  1223.  * Notes:   Blank old position and display new position.  current line and
  1224.  *          column may take up to 11 columns, which allows the display of
  1225.  *          999 columns and 9,999,999 lines.
  1226.  */
  1227. void show_line_col( WINDOW *window )
  1228. {
  1229. int i;
  1230. register int k;
  1231. char line_col[20], num[10];
  1232.  
  1233.    /*
  1234.     * blank out current line:column position.
  1235.     */
  1236.    memset( line_col, ' ', 12 );
  1237.    line_col[12] = '\0';
  1238.  
  1239.    /*
  1240.     * convert column to ascii and store in display buffer.
  1241.     */
  1242.    itoa( window->rcol+1, num, 10 );
  1243.    i = strlen( num ) - 1;
  1244.    for (k=11; i>=0; i--, k--)
  1245.       line_col[k] = num[i];
  1246.  
  1247.    /*
  1248.     * put in colon to separate line and column
  1249.     */
  1250.    line_col[k--] = ':';
  1251.  
  1252.    /*
  1253.     * convert line to ascii and store in display buffer.
  1254.     */
  1255.    ltoa( window->rline, num, 10 );
  1256.    i = strlen( num ) - 1;
  1257.    for (; i>=0; i--, k--)
  1258.       line_col[k] = num[i];
  1259.  
  1260.    /*
  1261.     * find line to start line:column display then output
  1262.     */
  1263.    s_output( line_col, window->top_line-1, window->end_col-11,
  1264.              g_display.head_color );
  1265.    show_asterisk( window );
  1266. }
  1267.  
  1268.  
  1269. /*
  1270.  * Name:    show_asterisk
  1271.  * Purpose: give user an indication if file is dirty
  1272.  * Date:    September 16, 1991
  1273.  * Passed:  window:  pointer to current window
  1274.  */
  1275. void show_asterisk( WINDOW *window )
  1276. {
  1277.    c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
  1278.              window->top_line-1, g_display.head_color );
  1279. }
  1280.  
  1281.  
  1282. /*
  1283.  * Name:    toggle_overwrite
  1284.  * Purpose: toggle overwrite-insert mode
  1285.  * Date:    September 16, 1991
  1286.  * Passed:  arg_filler:  argument to satify function prototype
  1287.  */
  1288. int  toggle_overwrite( WINDOW *arg_filler )
  1289. {
  1290.    mode.insert = !mode.insert;
  1291.    show_insert_mode( );
  1292.    set_cursor_size( mode.insert ? g_display.insert_cursor :
  1293.                     g_display.overw_cursor );
  1294.    return( OK );
  1295. }
  1296.  
  1297.  
  1298. /*
  1299.  * Name:    toggle_smart_tabs
  1300.  * Purpose: toggle smart tab mode
  1301.  * Date:    June 5, 1992
  1302.  * Passed:  arg_filler:  argument to satify function prototype
  1303.  */
  1304. int  toggle_smart_tabs( WINDOW *arg_filler )
  1305. {
  1306.    mode.smart_tab = !mode.smart_tab;
  1307.    show_tab_modes( );
  1308.    return( OK );
  1309. }
  1310.  
  1311.  
  1312. /*
  1313.  * Name:    toggle_indent
  1314.  * Purpose: toggle indent mode
  1315.  * Date:    September 16, 1991
  1316.  * Passed:  arg_filler:  argument to satify function prototype
  1317.  */
  1318. int  toggle_indent( WINDOW *arg_filler )
  1319. {
  1320.    mode.indent = !mode.indent;
  1321.    show_indent_mode( );
  1322.    return( OK );
  1323. }
  1324.  
  1325.  
  1326. /*
  1327.  * Name:    set_left_margin
  1328.  * Purpose: set left margin for word wrap
  1329.  * Date:    November 27, 1991
  1330.  * Passed:  window
  1331.  */
  1332. int  set_left_margin( WINDOW *window )
  1333. {
  1334. register int rc;
  1335. char temp[MAX_COLS];
  1336.  
  1337.    itoa( mode.left_margin + 1, temp, 10 );
  1338.    /*
  1339.     * enter left margin
  1340.     */
  1341.    rc = get_name( ed9, window->bottom_line, temp, g_display.message_color );
  1342.    if (rc == OK) {
  1343.       rc = atoi( temp ) - 1;
  1344.       if (rc < 0 || rc >= mode.right_margin) {
  1345.          /*
  1346.           * left margin out of range
  1347.           */
  1348.          error( WARNING, window->bottom_line, ed10 );
  1349.          rc = ERROR;
  1350.       } else {
  1351.          mode.left_margin = rc;
  1352.          show_all_rulers( );
  1353.       }
  1354.    }
  1355.    return( rc );
  1356. }
  1357.  
  1358.  
  1359. /*
  1360.  * Name:    set_right_margin
  1361.  * Purpose: set right margin for word wrap
  1362.  * Date:    November 27, 1991
  1363.  * Passed:  window
  1364.  */
  1365. int  set_right_margin( WINDOW *window )
  1366. {
  1367. register int rc;
  1368. char temp[MAX_COLS];
  1369.  
  1370.    itoa( mode.right_margin + 1, temp, 10 );
  1371.    /*
  1372.     * enter right margin
  1373.     */
  1374.    rc = get_name( ed11, window->bottom_line, temp, g_display.message_color );
  1375.    if (rc == OK) {
  1376.       rc = atoi( temp ) - 1;
  1377.       if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH) {
  1378.          /*
  1379.           * right margin out of range
  1380.           */
  1381.          error( WARNING, window->bottom_line, ed12 );
  1382.          rc = ERROR;
  1383.       } else {
  1384.          mode.right_margin = rc;
  1385.          show_all_rulers( );
  1386.       }
  1387.    }
  1388.    return( rc );
  1389. }
  1390.  
  1391.  
  1392. /*
  1393.  * Name:    set_paragraph_margin
  1394.  * Purpose: set column to begin paragraph
  1395.  * Date:    November 27, 1991
  1396.  * Passed:  window
  1397.  * Notes:   paragraph may be indented, flush, or offset.
  1398.  */
  1399. int  set_paragraph_margin( WINDOW *window )
  1400. {
  1401. register int rc;
  1402. char temp[80];
  1403.  
  1404.    itoa( mode.parg_margin + 1, temp, 10 );
  1405.    /*
  1406.     * enter paragraph margin
  1407.     */
  1408.    rc = get_name( ed13, window->bottom_line, temp, g_display.message_color );
  1409.    if (rc == OK) {
  1410.       rc = atoi( temp ) - 1;
  1411.       if (rc < 0 || rc >= mode.right_margin) {
  1412.          /*
  1413.           * paragraph margin out of range
  1414.           */
  1415.          error( WARNING, window->bottom_line, ed14 );
  1416.          rc = ERROR;
  1417.       } else {
  1418.          mode.parg_margin = rc;
  1419.          show_all_rulers( );
  1420.       }
  1421.    }
  1422.    return( rc );
  1423. }
  1424.  
  1425.  
  1426. /*
  1427.  * Name:    toggle_crlf
  1428.  * Purpose: toggle crlf mode
  1429.  * Date:    November 27, 1991
  1430.  * Passed:  arg_filler:  argument to satify function prototype
  1431.  */
  1432. int  toggle_crlf( WINDOW *arg_filler )
  1433. {
  1434.    mode.crlf = (mode.crlf == CRLF) ? LF : CRLF;
  1435.    show_crlf_mode( );
  1436.    return( OK );
  1437. }
  1438.  
  1439.  
  1440. /*
  1441.  * Name:    toggle_ww
  1442.  * Purpose: toggle word wrap mode
  1443.  * Date:    November 27, 1991
  1444.  * Passed:  arg_filler:  argument to satify function prototype
  1445.  */
  1446. int  toggle_ww( WINDOW *arg_filler )
  1447. {
  1448.    ++mode.word_wrap;
  1449.    if (mode.word_wrap > DYNAMIC_WRAP)
  1450.       mode.word_wrap = NO_WRAP;
  1451.    show_wordwrap_mode( );
  1452.    return( OK );
  1453. }
  1454.  
  1455.  
  1456. /*
  1457.  * Name:    toggle_trailing
  1458.  * Purpose: toggle eleminating trainling space at eol
  1459.  * Date:    November 25, 1991
  1460.  * Passed:  arg_filler:  argument to satify function prototype
  1461.  */
  1462. int  toggle_trailing( WINDOW *arg_filler )
  1463. {
  1464.    mode.trailing = !mode.trailing;
  1465.    show_trailing( );
  1466.    return( OK );
  1467. }
  1468.  
  1469.  
  1470. /*
  1471.  * Name:    toggle_z
  1472.  * Purpose: toggle writing control z at eof
  1473.  * Date:    November 25, 1991
  1474.  * Passed:  arg_filler:  argument to satify function prototype
  1475.  */
  1476. int  toggle_z( WINDOW *arg_filler )
  1477. {
  1478.    mode.control_z = !mode.control_z;
  1479.    show_control_z( );
  1480.    return( OK );
  1481. }
  1482.  
  1483.  
  1484. /*
  1485.  * Name:    toggle_eol
  1486.  * Purpose: toggle writing eol character at eol
  1487.  * Date:    November 25, 1991
  1488.  * Passed:  arg_filler:  argument to satify function prototype
  1489.  */
  1490. int  toggle_eol( WINDOW *arg_filler )
  1491. {
  1492. register file_infos *file;
  1493.  
  1494.    mode.show_eol = !mode.show_eol;
  1495.    for (file=g_status.file_list; file != NULL; file=file->next)
  1496.       file->dirty = GLOBAL;
  1497.    return( OK );
  1498. }
  1499.  
  1500.  
  1501. /*
  1502.  * Name:    toggle_search_case
  1503.  * Purpose: toggle search case
  1504.  * Date:    September 16, 1991
  1505.  * Passed:  arg_filler:  argument to satify function prototype
  1506.  */
  1507. int  toggle_search_case( WINDOW *arg_filler )
  1508. {
  1509.    mode.search_case = (mode.search_case == IGNORE) ? MATCH : IGNORE;
  1510.    show_search_case( );
  1511.    build_boyer_array( );
  1512.    return( OK );
  1513. }
  1514.  
  1515.  
  1516. /*
  1517.  * Name:    toggle_sync
  1518.  * Purpose: toggle sync mode
  1519.  * Date:    January 15, 1992
  1520.  * Passed:  arg_filler:  argument to satify function prototype
  1521.  */
  1522. int  toggle_sync( WINDOW *arg_filler )
  1523. {
  1524.    mode.sync = !mode.sync;
  1525.    show_sync_mode( );
  1526.    return( OK );
  1527. }
  1528.  
  1529.  
  1530. /*
  1531.  * Name:    toggle_ruler
  1532.  * Purpose: toggle ruler
  1533.  * Date:    March 5, 1992
  1534.  * Passed:  arg_filler:  argument to satify function prototype
  1535.  */
  1536. int  toggle_ruler( WINDOW *arg_filler )
  1537. {
  1538. register WINDOW *wp;
  1539.  
  1540.    mode.ruler = !mode.ruler;
  1541.    wp = g_status.window_list;
  1542.    while (wp != NULL) {
  1543.       if (mode.ruler) {
  1544.          /*
  1545.           * there has to be more than one line in a window to display a ruler.
  1546.           *   even if the ruler mode is on, we need to check the num of lines.
  1547.           */
  1548.          if (wp->bottom_line - wp->top_line >0) {
  1549.             if (wp->cline == wp->top_line)
  1550.                ++wp->cline;
  1551.             if (wp->cline > wp->bottom_line)
  1552.                wp->cline = wp->bottom_line;
  1553.             wp->ruler = TRUE;
  1554.          } else
  1555.             wp->ruler = FALSE;
  1556.       } else {
  1557.  
  1558.          /*
  1559.           * if this is the first page in a file, then we may need to "pull"
  1560.           *   the file up before displaying the first page.
  1561.           */
  1562.          if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
  1563.             --wp->cline;
  1564.          if (wp->cline < wp->top_line)
  1565.             wp->cline = wp->top_line;
  1566.          wp->ruler = FALSE;
  1567.       }
  1568.       make_ruler( wp );
  1569.       setup_window( wp );
  1570.       if (wp->visible)
  1571.          redraw_current_window( wp );
  1572.       wp = wp->next;
  1573.    }
  1574.    return( OK );
  1575. }
  1576.  
  1577.  
  1578. /*
  1579.  * Name:    toggle_tabinflate
  1580.  * Purpose: toggle inflating tabs
  1581.  * Date:    October 31, 1992
  1582.  * Passed:  arg_filler:  argument to satify function prototype
  1583.  */
  1584. int  toggle_tabinflate( WINDOW *arg_filler )
  1585. {
  1586. register file_infos *file;
  1587.  
  1588.    mode.inflate_tabs = !mode.inflate_tabs;
  1589.    for (file=g_status.file_list; file != NULL; file=file->next)
  1590.       file->dirty = GLOBAL;
  1591.    show_tab_modes( );
  1592.    return( OK );
  1593. }
  1594.  
  1595.  
  1596. /*
  1597.  * Name:    sync
  1598.  * Purpose: carry out cursor movements in all visible windows
  1599.  * Date:    January 15, 1992
  1600.  * Passed:  window
  1601.  * Notes:   switch sync semaphore when we do this so we don't get into a
  1602.  *          recursive loop.  All cursor movement commands un_copy_line before
  1603.  *          moving the cursor off the current line.   You MUST make certain
  1604.  *          that the current line is uncopied in the task routines before
  1605.  *          calling sync.
  1606.  */
  1607. void sync( WINDOW *window )
  1608. {
  1609. register WINDOW *wp;
  1610. register file_infos *fp;
  1611.  
  1612.    if (mode.sync && mode.sync_sem) {
  1613.       mode.sync_sem = FALSE;
  1614.       wp = g_status.window_list;
  1615.       while (wp != NULL) {
  1616.          if (wp->visible && wp != window) {
  1617.             (*do_it[g_status.command])( wp );
  1618.             show_line_col( wp );
  1619.             show_ruler_pointer( wp );
  1620.          }
  1621.          wp = wp->next;
  1622.       }
  1623.       mode.sync_sem = TRUE;
  1624.       fp = g_status.file_list;
  1625.       while (fp != NULL) {
  1626.          if (fp->dirty != FALSE)
  1627.             fp->dirty = GLOBAL;
  1628.          fp = fp->next;
  1629.       }
  1630.    }
  1631. }
  1632.  
  1633.  
  1634. /*
  1635.  * Name:    editor
  1636.  * Purpose: Set up the editor structures and display changes as needed.
  1637.  * Date:    June 5, 1991
  1638.  * Notes:   Master editor routine.
  1639.  */
  1640. void editor( )
  1641. {
  1642. char *name;  /* name of file to start editing */
  1643. register WINDOW *window;            /* current active window */
  1644. int c;
  1645.  
  1646.    /*
  1647.     * initialize search and seize
  1648.     */
  1649.    g_status.sas_defined = FALSE;
  1650.    for (c=0; c<SAS_P; c++)
  1651.       g_status.sas_arg_pointers[c] = NULL;
  1652.  
  1653.    /*
  1654.     * Check that user specified file to edit, if not offer help
  1655.     */
  1656.    if (g_status.argc > 1) {
  1657.       if (strcmp( g_status.argv[1], "/f" ) == 0  ||
  1658.                strcmp( g_status.argv[1], "-f" ) == 0) {
  1659.          /*
  1660.           * with search and seize their has to be at least 3 arg's, e.g.
  1661.           *    tde -p findme *.c
  1662.           */
  1663.          if (g_status.argc >= 4) {
  1664.             g_status.command = DefineSearchAndSeize;
  1665.             strcpy( sas_bm.pattern, g_status.argv[2] );
  1666.             for (c=3; c <= g_status.argc; c++)
  1667.                g_status.sas_arg_pointers[c-3] = g_status.argv[c];
  1668.             g_status.sas_argc = g_status.argc - 3;
  1669.             g_status.sas_arg = 0;
  1670.             g_status.sas_argv = g_status.sas_arg_pointers;
  1671.             g_status.sas_found_first = FALSE;
  1672.             g_status.sas_defined = TRUE;
  1673.             build_boyer_array( );
  1674.             bm.search_defined = sas_bm.search_defined = OK;
  1675.             c = search_and_seize( g_status.current_window );
  1676.          } else
  1677.             c = ERROR;
  1678.       } else
  1679.          c = edit_next_file( g_status.current_window );
  1680.    } else {
  1681.       name = g_status.rw_name;
  1682.       *name = '\0';
  1683.       /*
  1684.        * file name to edit
  1685.        */
  1686.       c = get_name( ed15, g_display.nlines, name, g_display.text_color );
  1687.       if (c == ERROR || *name == '\0')
  1688.          return;
  1689.       if (c == OK)
  1690.          c = attempt_edit_display( name, GLOBAL );
  1691.    }
  1692.    g_status.stop = c == OK ? FALSE : TRUE;
  1693.    if (c == OK)
  1694.       set_cursor_size( mode.insert ? g_display.insert_cursor :
  1695.                        g_display.overw_cursor );
  1696.  
  1697.    /*
  1698.     * main loop - keep updating the display and processing any commands
  1699.     *  while user has not pressed the stop key
  1700.     */
  1701.    for (; g_status.stop != TRUE;) {
  1702.       window = g_status.current_window;
  1703.       display_dirty_windows( window );
  1704.  
  1705.       /*
  1706.        * set the critical error handler flag to a known state before we
  1707.        *   do each editor command.
  1708.        */
  1709.       ceh.flag = OK;
  1710.  
  1711.       /*
  1712.        * Get a key from the user.  Look up the function assigned to that key.
  1713.        * All regular text keys are assigned to function 0.  Text characters
  1714.        * are less than 0x100, decimal 256, which includes the ASCII and
  1715.        * extended ASCII character set.
  1716.        */
  1717.       g_status.key_pressed = getkey( );
  1718.       g_status.command = getfunc( g_status.key_pressed );
  1719.       if (g_status.wrapped) {
  1720.          g_status.wrapped = FALSE;
  1721.          show_search_message( CLR_SEARCH, g_display.mode_color );
  1722.       }
  1723.       g_status.control_break = FALSE;
  1724.       if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
  1725.          record_keys( window->bottom_line );
  1726.          (*do_it[g_status.command])( window );
  1727.       }
  1728.    }
  1729.    cls( );
  1730.    xygoto( 0, 0 );
  1731. }
  1732.  
  1733.  
  1734. /*
  1735.  * Name:    display_dirty_windows
  1736.  * Purpose: Set up the editor structures and display changes as needed.
  1737.  * Date:    June 5, 1991
  1738.  * Notes:   Display all windows with dirty files.
  1739.  */
  1740. void display_dirty_windows( WINDOW *window )
  1741. {
  1742. register WINDOW *below;         /* window below current */
  1743. register WINDOW *above;         /* window above current */
  1744. file_infos *file;               /* temporary file structure */
  1745.  
  1746.    /*
  1747.     * update all windows that point to any file that has been changed
  1748.     */
  1749.    above = below = window;
  1750.    while (above->prev || below->next) {
  1751.       if (above->prev) {
  1752.          above = above->prev;
  1753.          show_dirty_window( above );
  1754.       }
  1755.       if (below->next) {
  1756.          below = below->next;
  1757.          show_dirty_window( below );
  1758.       }
  1759.    }
  1760.    file = window->file_info;
  1761.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  1762.       display_current_window( window );
  1763.    for (file=g_status.file_list; file != NULL; file=file->next)
  1764.       file->dirty = FALSE;
  1765.  
  1766.    /*
  1767.     * Set the cursor position at window->ccol, window->cline.  Show the
  1768.     * user where in the file the cursor is positioned.
  1769.     */
  1770.    xygoto( window->ccol, window->cline );
  1771.    show_line_col( window );
  1772.    show_ruler_pointer( window );
  1773. }
  1774.  
  1775.  
  1776.  
  1777. /*
  1778.  * Name:    show_dirty_window
  1779.  * Purpose: show changes in non-current window
  1780.  * Date:    June 5, 1991
  1781.  * Passed:  window:  pointer to current window
  1782.  */
  1783. void show_dirty_window( WINDOW *window )
  1784. {
  1785. register WINDOW *win;   /* register window pointer */
  1786. int dirty;
  1787.  
  1788.   win = window;
  1789.   if (win->visible) {
  1790.      dirty = win->file_info->dirty;
  1791.      if (dirty == GLOBAL || dirty == NOT_LOCAL) {
  1792.         display_current_window( win );
  1793.         show_size( win );
  1794.      }
  1795.      show_asterisk( win );
  1796.   }
  1797. }
  1798.  
  1799.  
  1800. /*
  1801.  * Name:    play_back
  1802.  * Purpose: play back a series of keystrokes assigned to key
  1803.  * Date:    April 1, 1992
  1804.  * Notes:   go thru the macro key list playing back the recorded keystrokes.
  1805.  *          to let macros call other macros, we have to 1) save the next
  1806.  *            keystroke of the current macro in a stack, 2) execute the
  1807.  *            the called macro, 3) pop the key that saved by the calling
  1808.  *            macro, 4) continue executing the macro, beginning with the
  1809.  *            key we just popped.
  1810.  *          use a local stack to store keys.  currently, there is room
  1811.  *            for 256 keys -- should be enough room for most purposes.
  1812.  */
  1813. int  play_back( WINDOW *window )
  1814. {
  1815. int key;
  1816. int rc = OK;
  1817. int popped;             /* flag is set when macro is popped */
  1818.  
  1819.    /*
  1820.     * if we are recording a macro, let's just return if we do a recursive
  1821.     *   definition.  Otherwise, we end up executing our recursive macro
  1822.     *   while we are defining it.
  1823.     */
  1824.    if (mode.record == TRUE && g_status.key_pressed == g_status.recording_key)
  1825.       rc = ERROR;
  1826.    else {
  1827.  
  1828.       /*
  1829.        * set the global macro flags, so other routines will know
  1830.        *   if a macro is executing.
  1831.        * set the stack_pointer to "empty" or -1.  initialize the popped
  1832.        *   flag to FALSE being that we haven't popped any thing off the stack,
  1833.        *   yet.
  1834.        */
  1835.       g_status.macro_executing = TRUE;
  1836.       g_status.mstack_pointer  = -1;
  1837.       popped = FALSE;
  1838.       rc = OK;
  1839.       while (rc == OK) {
  1840.  
  1841.          if (popped == FALSE) {
  1842.  
  1843.             /*
  1844.              * find the first keystroke in the macro.  when we pop the stack,
  1845.              *   all this stuff is reset by the pop -- do not reset it again.
  1846.              */
  1847.             g_status.macro_next = macro.first_stroke[g_status.key_pressed-256];
  1848.             g_status.current_macro = g_status.key_pressed;
  1849.             key = macro.strokes[g_status.macro_next].key;
  1850.          }
  1851.          popped = FALSE;
  1852.          if (key != MAX_KEYS+1  &&  key != -1) {
  1853.             do {
  1854.  
  1855.                /*
  1856.                 * set up all editor variables as if we were entering
  1857.                 *   keys from the keyboard.
  1858.                 */
  1859.                window = g_status.current_window;
  1860.                display_dirty_windows( window );
  1861.                ceh.flag = OK;
  1862.                g_status.key_pressed = macro.strokes[g_status.macro_next].key;
  1863.                g_status.command = getfunc( g_status.key_pressed );
  1864.                if (g_status.wrapped) {
  1865.                   g_status.wrapped = FALSE;
  1866.                   show_search_message( CLR_SEARCH, g_display.mode_color );
  1867.                }
  1868.  
  1869.                /*
  1870.                 * while there are no errors or Control-Breaks, let's keep on
  1871.                 *   executing a macro.  g_status.control_break is a global
  1872.                 *   editor flag that is set in our Control-Break interrupt
  1873.                 *   handler routine.
  1874.                 */
  1875.                if (g_status.control_break == TRUE) {
  1876.                   rc = ERROR;
  1877.                   break;
  1878.                }
  1879.  
  1880.                /*
  1881.                 * we haven't called any editor function yet.  we need
  1882.                 *   to look at the editor command that is to be executed.
  1883.                 *   if the command is PlayBack, we need to break out of
  1884.                 *   this inner do loop and start executing the macro
  1885.                 *   from the beginning (the outer do loop).
  1886.                 *
  1887.                 * if we don't break out now from a recursive macro, we will
  1888.                 *   recursively call PlayBack and we will likely overflow
  1889.                 *   the main (as opposed to the macro_stack) stack.
  1890.                 */
  1891.                if (g_status.command == PlayBack) {
  1892.  
  1893.                   /*
  1894.                    * recursive macros are handled differently from
  1895.                    *   macros that call other macros.
  1896.                    * recursive macros - break out of this inner loop
  1897.                    *   and begin executing the macro from the beg of macro.
  1898.                    * standard macros - save the next instruction of this
  1899.                    *   macro on the stack and begin executing the called macro.
  1900.                    */
  1901.                   if (g_status.current_macro != g_status.key_pressed) {
  1902.                      if (push_macro_stack(
  1903.                                    macro.strokes[g_status.macro_next].next )
  1904.                                    != OK) {
  1905.                         error( WARNING, window->bottom_line, ed16 );
  1906.                         rc = ERROR;
  1907.                      }
  1908.                      g_status.macro_next =
  1909.                                 macro.first_stroke[g_status.key_pressed-256];
  1910.                      g_status.current_macro = g_status.key_pressed;
  1911.                      key = macro.strokes[g_status.macro_next].key;
  1912.  
  1913.                      /*
  1914.                       * execute called macro at beginning of this do loop.
  1915.                       */
  1916.                      continue;
  1917.                   } else
  1918.  
  1919.                      /*
  1920.                       * recursive macro - break out of this inner loop
  1921.                       *   or else we may overflow the stack(s).
  1922.                       */
  1923.                      break;
  1924.                }
  1925.  
  1926.                if (g_status.command >= 0 && g_status.command < NUM_FUNCS)
  1927.                    rc = (*do_it[g_status.command])( window );
  1928.                g_status.macro_next =
  1929.                           macro.strokes[g_status.macro_next].next;
  1930.             } while (rc == OK  &&  g_status.macro_next != -1);
  1931.  
  1932.             /*
  1933.              * if we have come the end of a macro definition and there
  1934.              *   are no keys saved on the stack, we have finished our
  1935.              *   macro.  get out.
  1936.              */
  1937.             if (g_status.macro_next == -1 && g_status.mstack_pointer < 0)
  1938.                rc = ERROR;
  1939.             else if (rc != ERROR  &&  g_status.mstack_pointer >= 0) {
  1940.  
  1941.                /*
  1942.                 * if this is a recursive macro, don't pop the stack
  1943.                 *   because we didn't push.
  1944.                 * for a standard macro, get back the next key in the
  1945.                 *   calling macro.
  1946.                 */
  1947.                if (g_status.current_macro != g_status.key_pressed) {
  1948.                   if (pop_macro_stack( &g_status.macro_next ) != OK) {
  1949.                      error( WARNING, window->bottom_line, ed17 );
  1950.                      rc = ERROR;
  1951.                   } else {
  1952.                      popped = TRUE;
  1953.                      key = macro.strokes[g_status.macro_next].key;
  1954.                   }
  1955.                }
  1956.             }
  1957.          }
  1958.       }
  1959.       g_status.macro_executing = FALSE;
  1960.    }
  1961.    return( OK );
  1962. }
  1963.  
  1964.  
  1965. /*
  1966.  * Name:    push_macro_stack
  1967.  * Purpose: push the next key in a currently executing macro on local stack
  1968.  * Date:    October 31, 1992
  1969.  * Notes:   finally got tired of letting macros only "jump" and not call
  1970.  *            other macros.
  1971.  *          the first time in, stack_pointer is -1.
  1972.  */
  1973. int  push_macro_stack( int key )
  1974. {
  1975.    /*
  1976.     * first, make sure we have room to push the key.
  1977.     */
  1978.    if (g_status.mstack_pointer+1 < MAX_KEYS) {
  1979.  
  1980.       /*
  1981.        * increment the stack pointer and store the pointer to the next key
  1982.        *   of the currently executing macro.  store the currently executing
  1983.        *   macro, too.
  1984.        */
  1985.       ++g_status.mstack_pointer;
  1986.       macro_stack[g_status.mstack_pointer].key = key;
  1987.       macro_stack[g_status.mstack_pointer].macro = g_status.current_macro;
  1988.       return( OK );
  1989.    } else
  1990.       return( STACK_OVERFLOW );
  1991. }
  1992.  
  1993.  
  1994. /*
  1995.  * Name:    pop_macro_stack
  1996.  * Purpose: pop currently executing macro on local stack
  1997.  * Date:    October 31, 1992
  1998.  * Notes:   finally got tired of letting macros only "jump" and not "call"
  1999.  *            other macros.
  2000.  *          pop the macro stack.  stack pointer is pointing to last key saved
  2001.  *            on stack.
  2002.  */
  2003. int  pop_macro_stack( int *key )
  2004. {
  2005.  
  2006.    /*
  2007.     * before we pop the stack, make sure there is something in the stack.
  2008.     */
  2009.    if (g_status.mstack_pointer >= 0) {
  2010.  
  2011.       /*
  2012.        * pop the pointer to the next key and the current macro, then
  2013.        *   decrement the stack_pointer.
  2014.        */
  2015.       *key = macro_stack[g_status.mstack_pointer].key;
  2016.       g_status.current_macro = macro_stack[g_status.mstack_pointer].macro;
  2017.       --g_status.mstack_pointer;
  2018.       return( OK );
  2019.    } else
  2020.       return( STACK_UNDERFLOW );
  2021. }
  2022.  
  2023.  
  2024. /*
  2025.  * Name:    Pause
  2026.  * Purpose: Enter pause state for macros
  2027.  * Date:    June 5, 1992
  2028.  * Passed:  arg_filler:  argument to satify function prototype
  2029.  * Returns: ERROR if the ESC key was pressed, OK otherwise.
  2030.  * Notes:   this little function is quite useful in macro definitions.  if
  2031.  *          it is called near the beginning of a macro, the user may decide
  2032.  *          whether or not to stop the macro.
  2033.  */
  2034. int  pause( WINDOW *arg_filler )
  2035. {
  2036. int c;
  2037.  
  2038.    /*
  2039.     * tell user we are paused.
  2040.     */
  2041.    s_output( paused1, g_display.mode_line, 23, g_display.mode_color | 0x80 );
  2042.    s_output( paused2, g_display.mode_line, 23+strlen( paused1 ),
  2043.              g_display.mode_color );
  2044.  
  2045.    /*
  2046.     * get the user's response and restore the mode line.
  2047.     */
  2048.    c = getkey( );
  2049.    show_modes( );
  2050.    if (mode.record == TRUE) {
  2051.       /*
  2052.        * if recording a macro, show recording message
  2053.        */
  2054.       s_output( main15, g_display.mode_line, 23, g_display.mode_color | 0x80 );
  2055.       show_avail_strokes( );
  2056.    }
  2057.    return( c == ESC ? ERROR : OK );
  2058. }
  2059.